unit searchresults;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls;

type

  TFileSearchResult = record
    findcount: integer;
    locations: array of TPoint;
  end;


  { TSearchResultsForm }

  TSearchResultsForm = class ( TForm )
    TreeView1: TTreeView;
    procedure TreeView1Click ( Sender: TObject ) ;
  private
    { private declarations }
  public
    { public declarations }
    function DoSearch( searchstr, replacestr, directory, filemask: string ): integer;
    function DoAdvancedSearch( searchstr, replacestr, directory, filemask: string ): integer;
    function Searchfile(aFile: TFilename; SubStr: string): TFileSearchResult;
    function WildcardSearchfile(aFile: TFilename; SearchStr, ReplaceStr: string): TFileSearchResult;
  end;




var
  SearchResultsForm: TSearchResultsForm;

implementation

{$R *.lfm}

{ TSearchResultsForm }

function TSearchResultsForm.Searchfile ( aFile: TFilename; SubStr: string
  ) : TFileSearchResult;
var
  F: longint;
  R: TFileSearchResult;
  Y, X: integer;
  MaxLen: SizeInt;
  buf: array[0..4095] of char;
  i: Integer;
  pc: PChar;
  bytesread: Int64;
  loc: TPoint;
  filenode: TTreeNode;
begin
  F := FileOpenUTF8(aFile, fmOpenRead);
  SetLength(R.locations, 50);
  Y := 1;
  X := 1;
  R.findcount := 0;
  SetLength(R.locations, 100);
  bytesread := FileRead(F, buf, 4096);
  if Length(SubStr)>0 then
  while bytesread>0 do
   begin
     MaxLen:=sizeint(bytesread)-Length(SubStr);
     i := 0;
     pc := @buf[1];
     while (i<=MaxLen) do
      begin
        inc(i);
        if (SubStr[1]=pc^) and
           (CompareChar(Substr[1],pc^,Length(SubStr))=0) then
             begin   // match found
               if R.findcount<1 then filenode := TreeView1.Items.Add(nil, ExtractFileName(aFile));
               R.locations[R.findcount].X := X;
               R.locations[R.findcount].Y := Y;
               loc := R.locations[R.findcount];
               TreeView1.Items.AddChild(filenode, '(' + IntToStr(Y) + ',' + IntToStr(X) + '): ');
               Inc(R.findcount);
             end;
        if (pc^=#10) then
             begin  //  newline
               Inc(Y);
               X := 0;
             end;
        inc(pc);
        inc(X);
      end;
     bytesread := FileRead(F, buf, 4096);
   end;
  FileClose(F);
  Result := R;
end;

function TSearchResultsForm.WildcardSearchfile ( aFile: TFilename;
  SearchStr, ReplaceStr: string ) : TFileSearchResult;
const BUFSIZE = 1024;
var
  F: longint;
  R: TFileSearchResult;
  Y, X: integer;
  MaxLen: SizeInt;
  buf: array[0..BUFSIZE-1] of char;
  i, j: Integer;
  pc: PChar;
  bytesread: Int64;
  filenode: TTreeNode;
  Substr: array[0..9] of String;
  substrcount: integer;
  substrtofind: integer;
  posinfile: Integer;
  startblock: Integer;
  endblock: Integer;
  outfile: TFileStream;
  bytescopied: LongInt;
  sectionstart: Integer;
  searchblockstart: Integer;
begin
  F := FileOpenUTF8(aFile, fmOpenRead);
  SetLength(R.locations, 50);
  Y := 1;
  X := 1;
  posinfile := 0;
  startblock := 0;
  endblock := 0;
  substrcount := 0;
  sectionstart := 0;
  R.findcount := 0;
  searchblockstart := 0;
  outfile := nil;
  SetLength(R.locations, 100);
  bytesread := FileRead(F, buf[0], BUFSIZE);
  i := Pos('.*', SearchStr);
  while i>0 do
        begin
          Substr[substrcount] := Copy(SearchStr, 1, i-1);
          Delete(SearchStr, 1, i+1);
          inc(substrcount);
          i := Pos('.*', SearchStr);
        end;
  if Length(SearchStr)>0 then
     begin
       Substr[substrcount] := SearchStr;
       Inc(substrcount);
     end;
  i := 0;
  j := 0;

  substrtofind := 0;
  if Length(SubStr[0])>0 then
  while bytesread>0 do
   begin
     MaxLen:=sizeint(bytesread)-Length(Substr[substrtofind]);
     i := 0;
     pc := @buf[0];
     while (i<=MaxLen) do
      begin
        inc(i);
        if (SubStr[substrtofind][1]=pc^) and
           (CompareChar(Substr[substrtofind][1],pc^,Length(SubStr[substrtofind]))=0) then
             begin   // match found
               R.locations[R.findcount].X := X;
               R.locations[R.findcount].Y := Y;
               if substrtofind=0 then startblock := posinfile+1;
               Inc(substrtofind);
               if substrtofind=substrcount then
                  begin
                    substrtofind := 0;
                    endblock := posinfile + Length(Substr[substrcount-1]);
                    if R.findcount<1 then filenode := TreeView1.Items.Add(nil, ExtractFileName(aFile));
                    TreeView1.Items.AddChild(filenode, '(' + IntToStr(Y) + ',' + IntToStr(X) + '): ');
                    Inc(R.findcount);
                    if Length(ReplaceStr)> 0 then
                      begin
                        if outfile=nil then outfile := TFileStream.Create('/tmp/test.htm', fmOpenWrite or fmCreate);
                        FileSeek(F, sectionstart, fsFromBeginning);
                        bytescopied := FileRead(F, buf[0], BUFSIZE);
                        while bytescopied+sectionstart<startblock do
                           begin
                             outfile.Write(buf[0], bytescopied);
                             sectionstart := sectionstart+bytescopied;
                             bytescopied := FileRead(F, buf[0], BUFSIZE);
                           end;
                        outfile.Write(buf[0], startblock-sectionstart-1);
                        outfile.Write(ReplaceStr[1], Length(ReplaceStr));
                        sectionstart := endblock;
                        FileSeek(F, searchblockstart, fsFromBeginning);
                        FileRead(F, buf[0], BUFSIZE);
                      end;
                  end;
               MaxLen := sizeint(bytesread)-Length(Substr[substrtofind]);
             end;
        if (pc^=#10) then
             begin  //  newline
               Inc(Y);
               X := 0;
             end;
        Inc(posinfile);
        inc(pc);
        inc(X);
      end;
     for j := i to BUFSIZE do
       begin
         buf[j-i] := buf[j];
       end;
     searchblockstart := posinfile;
     bytesread := FileRead(F, buf[j-i], BUFSIZE+i-j);
     if bytesread>0 then bytesread := bytesread + j-i;
   end;
  if outfile<>nil then
     begin
       FileSeek(F, sectionstart, fsFromBeginning);
       bytescopied := FileRead(F, buf, BUFSIZE);
       while bytescopied>0 do
          begin
            outfile.Write(buf[0], bytescopied);
            bytescopied := FileRead(F, buf, 4096);
          end;
       outfile.Free;
     end;
  FileClose(F);
  Result := R;
end;

procedure TSearchResultsForm.TreeView1Click ( Sender: TObject ) ;
var
  P: Pointer;
begin
  {P := TreeView1.Selected.Data;
  ShowMessage(IntToStr(TPoint(P^).X));}
end;

function TSearchResultsForm.DoSearch ( searchstr, replacestr, directory,
  filemask: string ) : integer;
var
  FileList : TSearchRec;
  TempFile: TStringList;
  loc: SizeInt;
  p: Integer;
  S: string;
  filenode: TTreeNode;
  lin: String;

  function Extractline(startpos: Integer): string;
  var p1, p2: integer;
    Testchar: Char;
  begin
    p1 := startpos;
    while (p1>0) and not (TempFile.Text[p1] in [#10, #13]) do
       Dec(p1);
    p2 := startpos;
    while (p2<Length(Tempfile.Text)) and not (TempFile.Text[p2] in [#10, #13]) do
       begin Inc(p2); Testchar := TempFile.Text[p2]; end;;
    Result := Copy(TempFile.Text, p1 + 1, p2-p1-1);
  end;
begin
  Result := 0;
  if FindFirst(directory + DirectorySeparator + filemask, faAnyFile, FileList) = 0 then
  begin
    TempFile := TStringList.Create;
    repeat
      TempFile.LoadFromFile(directory + DirectorySeparator + FileList.Name);
      S := TempFile.Text;
      loc := Pos(searchstr, TempFile.Text);
      p := 0;
      if loc>0 then
         filenode := TreeView1.Items.Add(nil, FileList.Name);
      while loc>0 do
         begin
           Inc(Result);
           lin := Extractline(loc+p);
           TreeView1.Items.AddChild(filenode, '(' + IntToStr(TempFile.IndexOf(lin)) + ',' + IntToStr(Pos(searchstr, lin)) + '): ' + Trim(lin));
           Delete(S, 1, loc + Length(searchstr)-1);
           p := p + loc + Length(searchstr);
           loc := Pos(searchstr, S);
         end;
    until FindNext(FileList) <> 0;

    // Must free up resources used by these successful finds
    FindClose(FileList);
    TempFile.Free;
  end;

end;

function TSearchResultsForm.DoAdvancedSearch ( searchstr, replacestr,
  directory, filemask: string ) : integer;
var
  FileList : TSearchRec;
  aFile: Text;
  buf: array[0..4095] of char;
begin
  Result := 0;
  directory := IncludeTrailingPathDelimiter(directory);
  if FindFirst(directory + filemask, faAnyFile, FileList) = 0 then
  begin

    repeat
   //  if Pos('.*', searchstr)>0 then
     result := result + WildcardSearchfile(directory + FileList.Name, searchstr, replacestr).findcount ;
    // else Result := Result + Searchfile(directory + FileList.Name, searchstr).findcount;
    until FindNext(FileList) <> 0;

    // Must free up resources used by these successful finds
    FindClose(FileList);
  end;
end;

end.

